package com.jack.webrtcsimple

import android.content.Context
import android.media.AudioManager
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.jack.webrtcsimple.signal.MyIceServer
import com.jack.webrtcsimple.signal.RTCSignalClient
import com.jack.webrtcsimple.signal.RTCSignalClient.OnSignalEventListener
import org.json.JSONException
import org.json.JSONObject
import org.webrtc.AudioTrack
import org.webrtc.Camera1Enumerator
import org.webrtc.Camera2Enumerator
import org.webrtc.CameraEnumerator
import org.webrtc.DataChannel
import org.webrtc.DefaultVideoDecoderFactory
import org.webrtc.DefaultVideoEncoderFactory
import org.webrtc.EglBase
import org.webrtc.IceCandidate
import org.webrtc.Logging
import org.webrtc.MediaConstraints
import org.webrtc.MediaStream
import org.webrtc.PeerConnection
import org.webrtc.PeerConnection.IceConnectionState
import org.webrtc.PeerConnection.IceGatheringState
import org.webrtc.PeerConnection.IceServer
import org.webrtc.PeerConnection.RTCConfiguration
import org.webrtc.PeerConnection.SignalingState
import org.webrtc.PeerConnectionFactory
import org.webrtc.RendererCommon
import org.webrtc.RtpReceiver
import org.webrtc.SdpObserver
import org.webrtc.SessionDescription
import org.webrtc.SurfaceTextureHelper
import org.webrtc.SurfaceViewRenderer
import org.webrtc.VideoCapturer
import org.webrtc.VideoDecoderFactory
import org.webrtc.VideoEncoderFactory
import org.webrtc.VideoFrame
import org.webrtc.VideoSink
import org.webrtc.VideoTrack
import java.util.UUID
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

/**
 * 本地视图跟远端视图 是 一样的
 */
class CallActivity : AppCompatActivity() {
    private var mLogcatView: TextView? = null
    private var mStartCallBtn: TextView? = null
    private var mEndCallBtn: TextView? = null

    private var mRootEglBase: EglBase? = null

    private var mPeerConnection: PeerConnection? = null // 类似js的RTCPeerConnection
    private var mPeerConnectionFactory: PeerConnectionFactory? = null
    private var mLocalStream: MediaStream? = null

    private var mSurfaceTextureHelper: SurfaceTextureHelper? = null

    private var mLocalSurfaceView: SurfaceViewRenderer? = null
    private var mRemoteSurfaceView: SurfaceViewRenderer? = null

    private var mVideoTrack: VideoTrack? = null

    //8.梳理 mAudioTrack
    private var mAudioTrack: AudioTrack? = null

    private var mVideoCapturer: VideoCapturer? = null //视频捕获

    private var mRemoteUserId: String? = null

    private var mExecutor: ExecutorService? = null
    private var mRoomId: String? = null
    private var mIsJoinRoom = false

    private var ICEServers: ArrayList<IceServer?>? = null
    private var mAudioManager: AudioManager? = null
    private var mSignalClient: RTCSignalClient? = null

    private fun createAudioConstraints(): MediaConstraints {
        val audioConstraints = MediaConstraints()
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_ECHO_CANCELLATION_CONSTRAINT, "true")
        )
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT, "false")
        )
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_HIGH_PASS_FILTER_CONSTRAINT, "true")
        )
        audioConstraints.mandatory.add(
            MediaConstraints.KeyValuePair(AUDIO_NOISE_SUPPRESSION_CONSTRAINT, "true")
        )
        return audioConstraints
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_call)

        mLogcatView = findViewById<TextView>(R.id.LogcatView)
        mStartCallBtn = findViewById<TextView>(R.id.StartCallButton)
        mEndCallBtn = findViewById<TextView>(R.id.EndCallButton)

        mExecutor = Executors.newSingleThreadExecutor()

        mSignalClient = RTCSignalClient(mOnSignalEventListener) // 创建信令

        mAudioManager = getSystemService(AUDIO_SERVICE) as AudioManager
        mAudioManager!!.setSpeakerphoneOn(true) //喇叭外放
        mAudioManager!!.setMicrophoneMute(false) //麦克风静止

        mRoomId = getIntent().getStringExtra("roomId")

        //mRootEglBase 用来做显示 用的
        mRootEglBase = EglBase.create()

        mLocalSurfaceView = findViewById<SurfaceViewRenderer>(R.id.LocalSurfaceView)
        mRemoteSurfaceView = findViewById<SurfaceViewRenderer>(R.id.RemoteSurfaceView)

        mLocalSurfaceView!!.init(mRootEglBase!!.getEglBaseContext(), null)
        mLocalSurfaceView!!.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL)
        mLocalSurfaceView!!.setMirror(true)
        mLocalSurfaceView!!.setEnableHardwareScaler(false /* enabled */)

        mRemoteSurfaceView!!.init(mRootEglBase!!.getEglBaseContext(), null)
        mRemoteSurfaceView!!.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL)
        mRemoteSurfaceView!!.setMirror(true)
        mRemoteSurfaceView!!.setEnableHardwareScaler(true /* enabled */)
        mRemoteSurfaceView!!.setZOrderMediaOverlay(true)

        val videoSink = ProxyVideoSink()
        videoSink.setTarget(mLocalSurfaceView) //3.videoSink  设置 显示 视图

        mPeerConnectionFactory = createPeerConnectionFactory(this)

        // NOTE: this _must_ happen while PeerConnectionFactory is alive!
        Logging.enableLogToDebugOutput(Logging.Severity.LS_VERBOSE)

        //1.创建mVideoCapturer  视频采集  createVideoCapturer的api比较直观
        mVideoCapturer = createVideoCapturer()

        mSurfaceTextureHelper =
            SurfaceTextureHelper.create("CaptureThread", mRootEglBase!!.getEglBaseContext())
        val videoSource = mPeerConnectionFactory!!.createVideoSource(false)
        //2.videoSource 的数据从 mVideoCapturer 中来的，需要将videoSource 传入到 mVideoCapturer 中
        mVideoCapturer!!.initialize(
            mSurfaceTextureHelper,
            getApplicationContext(),
            videoSource.getCapturerObserver()
        )

        //4.将 videoSource 传递给 mVideoTrack
        mVideoTrack = mPeerConnectionFactory!!.createVideoTrack(VIDEO_TRACK_ID, videoSource)
        mVideoTrack!!.setEnabled(true)
        //5.接着将 videoSink 传入到 mVideoTrack
        //mVideoTrack 通过 videoSink 做显示，videoSink 是做了一个代理，最终的显示是会显示在 mLocalSurfaceView， mLocalSurfaceView 就显示在本地视图
        mVideoTrack!!.addSink(videoSink)

        val audioSource = mPeerConnectionFactory!!.createAudioSource(createAudioConstraints())
        //8.1.通过 mPeerConnectionFactory 的工厂类  关联 audioSource
        //9.关注 mPeerConnectionFactory 的来源
        mAudioTrack = mPeerConnectionFactory!!.createAudioTrack(AUDIO_TRACK_ID, audioSource)
        mAudioTrack!!.setEnabled(true)

        //10.创建一个本地流
        mLocalStream = mPeerConnectionFactory!!.createLocalMediaStream(LOCAL_STREAM_ID)

        // 本地流从mAudioTrack + mVideoTrack        下一步分析：mPeerConnection 的来源
        mLocalStream!!.addTrack(mAudioTrack)
        mLocalStream!!.addTrack(mVideoTrack)
    }

    override fun onResume() {
        super.onResume()
        //开始采集
        mVideoCapturer!!.startCapture(VIDEO_RESOLUTION_WIDTH, VIDEO_RESOLUTION_HEIGHT, VIDEO_FPS)
    }

    override fun onPause() {
        super.onPause()
        try {
            //停止采集
            mVideoCapturer!!.stopCapture()
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (mIsJoinRoom) {
            mIsJoinRoom = false
            doEndCall()
        }
        mLocalSurfaceView!!.release()
        mRemoteSurfaceView!!.release()
        //释放
        mVideoCapturer!!.dispose()
        mSurfaceTextureHelper!!.dispose()
        PeerConnectionFactory.stopInternalTracingCapture()
        PeerConnectionFactory.shutdownInternalTracer()

        mSignalClient!!.disConnect()
    }

    class ProxyVideoSink : VideoSink {
        private var mTarget: VideoSink? = null

        @Synchronized
        override fun onFrame(frame: VideoFrame?) {
            if (mTarget == null) {
                Log.d(TAG, "Dropping frame in proxy because target is null.")
                return
            }
            mTarget!!.onFrame(frame)
        }

        @Synchronized
        fun setTarget(target: VideoSink?) {
            this.mTarget = target
        }
    }

    open class SimpleSdpObserver : SdpObserver {
        override fun onCreateSuccess(sessionDescription: SessionDescription?) {
            Log.i(TAG, "SdpObserver: onCreateSuccess !")
        }

        override fun onSetSuccess() {
            Log.i(TAG, "SdpObserver: onSetSuccess")
        }

        override fun onCreateFailure(msg: String?) {
            Log.e(TAG, "SdpObserver onCreateFailure: " + msg)
        }

        override fun onSetFailure(msg: String?) {
            Log.e(TAG, "SdpObserver onSetFailure: " + msg)
        }
    }

    fun onClickStartCallButton(v: View?) {
        Log.i(TAG, "onClickStartCallButton")
        mSignalClient!!.joinRoom(UUID.randomUUID().toString(), mRoomId)
        mIsJoinRoom = true
        updateCallState(false)
    }

    fun onClickEndCallButton(v: View?) {
        Log.i(TAG, "onClickEndCallButton")
        doEndCall()
        mIsJoinRoom = false
        updateCallState(true)
    }

    private fun updateCallState(idle: Boolean) {
        runOnUiThread(object : Runnable {
            override fun run() {
                if (idle) {
                    mStartCallBtn!!.setVisibility(View.VISIBLE)
                    mEndCallBtn!!.setVisibility(View.GONE)
                    mRemoteSurfaceView!!.setVisibility(View.GONE)
                } else {
                    mStartCallBtn!!.setVisibility(View.GONE)
                    mEndCallBtn!!.setVisibility(View.VISIBLE)
                    mRemoteSurfaceView!!.setVisibility(View.VISIBLE)
                }
            }
        })
    }

    fun doStartCall() {
        logcatOnUI("Start Call, Wait ...")
        if (mPeerConnection == null) {
            //11.mPeerConnection 的来源
            mPeerConnection = createPeerConnection()
        }
        val mediaConstraints = MediaConstraints()
        mediaConstraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
        mediaConstraints.mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"))
        mediaConstraints.optional.add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))
        //重点关注
        mPeerConnection!!.createOffer(object : SimpleSdpObserver() {
            override fun onCreateSuccess(sessionDescription: SessionDescription?) {
                Log.i(TAG, "Create local offer success: \n" + sessionDescription?.description)
                //15.设置本地 sdp
                mPeerConnection!!.setLocalDescription(SimpleSdpObserver(), sessionDescription)
                //16.发送 sdp   其格式跟 js 存在一点点区别
                mSignalClient!!.sendOffer(
                    toJsonSessionDescription(sessionDescription),
                    mRemoteUserId
                )
            }
        }, mediaConstraints)
    }

    fun doEndCall() {
        if (!mIsJoinRoom) {
            Log.w(TAG, "no join room")
        }
        logcatOnUI("End Call, Wait ...")
        hanup()
        mSignalClient!!.leaveRoom()
    }

    fun doAnswerCall(description: String?) {
        logcatOnUI("Answer Call, Wait ...")
        if (mPeerConnection == null) {
            mPeerConnection = createPeerConnection()
        }
        mPeerConnection!!.setRemoteDescription(
            SimpleSdpObserver(),
            SessionDescription(SessionDescription.Type.OFFER, description)
        )

        val sdpMediaConstraints = MediaConstraints()
        val mediaConstraints = MediaConstraints()
        val keyValuePairs = ArrayList<MediaConstraints.KeyValuePair?>()
        keyValuePairs.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
        keyValuePairs.add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"))
        mediaConstraints.mandatory.addAll(keyValuePairs)
        Log.i(TAG, "Create answer ...")
        //17.createAnswer 同 createOffer 类似
        mPeerConnection!!.createAnswer(object : SimpleSdpObserver() {
            override fun onCreateSuccess(sessionDescription: SessionDescription?) {
                Log.i(TAG, "Create answer success !")
                mPeerConnection!!.setLocalDescription(SimpleSdpObserver(), sessionDescription)
                mSignalClient!!.sendAnswer(
                    toJsonSessionDescription(sessionDescription),
                    mRemoteUserId
                )
            }
        }, sdpMediaConstraints)
        updateCallState(false)
    }

    private fun hanup() {
        logcatOnUI("Hanup Call, Wait ...")
        if (mPeerConnection == null) {
            return
        }
        mPeerConnection!!.close()
        mPeerConnection = null
        logcatOnUI("Hanup Done.")
        updateCallState(true)
    }

    fun createPeerConnection(): PeerConnection? {
        Log.i(TAG, "Create PeerConnection ...")
        this.ICEServers = ArrayList<IceServer?>()
        if (iceServers != null) {
            for (myIceServer in iceServers) {
                val iceServer = IceServer
                    .builder(myIceServer.uri)
                    .setUsername(myIceServer.username)
                    .setPassword(myIceServer.password)
                    .createIceServer()
                ICEServers!!.add(iceServer)
            }
        }

        val configuration = RTCConfiguration(ICEServers) // 传入coturn的stun和turn的地址
        //12.通过类比法来学习  在学习Android端的webrtc之前，先将js端的webrtc 一对一 视频通话掌握
//        configuration.iceTransportsType = PeerConnection.IceTransportsType.RELAY;   // 设置为中继模式
        configuration.iceTransportsType =
            PeerConnection.IceTransportsType.ALL // 设置为中继模式   局域网测试需要设置为ALL
        //跟js 存在一定的区别，传入的参数1： configuration 跟js相同
        //传入的参数2：mPeerConnectionObserver（将该对象注册到PeerConnection当中去，观察PeerConnection发生的变化。当相应的事件发生变化则进行通知回调）   关注内部的 onIceCandidate（同js的类似） 和 onAddTrack
        val connection =
            mPeerConnectionFactory!!.createPeerConnection(configuration, mPeerConnectionObserver)
        if (connection == null) {
            Log.e(TAG, "Failed to createPeerConnection !")
            return null
        }
        //创建的流通过 addStream 添加到 PeerConnection 当中去
        connection.addStream(mLocalStream)
        //        connection.addTrack(mVideoTrack);
//        connection.addTrack(mAudioTrack);
        return connection
    }

    //几乎都是 约定 好的
    fun createPeerConnectionFactory(context: Context?): PeerConnectionFactory {
        val encoderFactory: VideoEncoderFactory //编码
        val decoderFactory: VideoDecoderFactory //解码

        encoderFactory = DefaultVideoEncoderFactory(
            mRootEglBase!!.getEglBaseContext(), false,  /* enableIntelVp8Encoder */true
        )
        decoderFactory = DefaultVideoDecoderFactory(mRootEglBase!!.getEglBaseContext())

        PeerConnectionFactory.initialize(
            PeerConnectionFactory.InitializationOptions.builder(context)
                .setEnableInternalTracer(true)
                .createInitializationOptions()
        )

        val builder = PeerConnectionFactory.builder()
            .setVideoEncoderFactory(encoderFactory)
            .setVideoDecoderFactory(decoderFactory)
        builder.setOptions(null)

        return builder.createPeerConnectionFactory()
    }

    /*
     * Read more about Camera2 here
     * https://developer.android.com/reference/android/hardware/camera2/package-summary.html
     **/
    private fun createVideoCapturer(): VideoCapturer? {
        if (Camera2Enumerator.isSupported(this)) {
            return createCameraCapturer(Camera2Enumerator(this))
        } else {
            return createCameraCapturer(Camera1Enumerator(true))
        }
    }

    private fun createCameraCapturer(enumerator: CameraEnumerator): VideoCapturer? {
        val deviceNames = enumerator.getDeviceNames()

        // First, try to find front facing camera
        Log.d(TAG, "Looking for front facing cameras.")
        for (deviceName in deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating front facing camera capturer.")
                val videoCapturer: VideoCapturer? = enumerator.createCapturer(deviceName, null)
                if (videoCapturer != null) {
                    return videoCapturer
                }
            }
        }

        // Front facing camera not found, try something else
        Log.d(TAG, "Looking for other cameras.")
        for (deviceName in deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating other camera capturer.")
                val videoCapturer: VideoCapturer? = enumerator.createCapturer(deviceName, null)
                if (videoCapturer != null) {
                    return videoCapturer
                }
            }
        }
        return null
    }

    private val mPeerConnectionObserver: PeerConnection.Observer =
        object : PeerConnection.Observer {
            override fun onSignalingChange(signalingState: SignalingState?) {
                Log.i(TAG, "onSignalingChange: " + signalingState)
            }

            override fun onIceConnectionChange(iceConnectionState: IceConnectionState?) {
                Log.i(TAG, "onIceConnectionChange: " + iceConnectionState)
            }

            override fun onIceConnectionReceivingChange(b: Boolean) {
                Log.i(TAG, "onIceConnectionChange: " + b)
            }

            override fun onIceGatheringChange(iceGatheringState: IceGatheringState?) {
                Log.i(TAG, "onIceGatheringChange: " + iceGatheringState)
            }

            override fun onIceCandidate(iceCandidate: IceCandidate) {
                Log.i(TAG, "onIceCandidate: " + iceCandidate)

                //13. 信令服务器客户端 转发
                mSignalClient!!.sendCandidate(toJsonCandidate(iceCandidate), mRemoteUserId)
            }

            override fun onIceCandidatesRemoved(iceCandidates: Array<IceCandidate?>) {
                for (i in iceCandidates.indices) {
                    Log.i(TAG, "onIceCandidatesRemoved: " + iceCandidates[i])
                }
                mPeerConnection!!.removeIceCandidates(iceCandidates)
            }

            override fun onAddStream(mediaStream: MediaStream) {
                Log.i(TAG, "onAddStream: " + mediaStream.videoTracks.size)
            }

            override fun onRemoveStream(mediaStream: MediaStream?) {
                Log.i(TAG, "onRemoveStream")
            }

            override fun onDataChannel(dataChannel: DataChannel?) {
                Log.i(TAG, "onDataChannel")
            }

            override fun onRenegotiationNeeded() {
                Log.i(TAG, "onRenegotiationNeeded")
            }

            override fun onAddTrack(rtpReceiver: RtpReceiver, mediaStreams: Array<MediaStream?>?) {
                val track = rtpReceiver.track()
                if (track is VideoTrack) {  // 判别是不是VideoTrack
                    Log.i(TAG, "onAddVideoTrack")
                    val remoteVideoTrack = track
                    remoteVideoTrack.setEnabled(true)
                    val videoSink = ProxyVideoSink()

                    //6.远端显示： 对方码流过来的时候就会将 加载到 mRemoteSurfaceView
                    videoSink.setTarget(mRemoteSurfaceView)
                    //7.将显示关联起来
                    remoteVideoTrack.addSink(videoSink)
                }
            }
        }

    private val mOnSignalEventListener: OnSignalEventListener = object : OnSignalEventListener {
        override fun onConnected() {
            logcatOnUI("Signal Server Connected !")
        }

        override fun onConnecting() {
            logcatOnUI("Signal Server Connecting !")
        }

        override fun onDisconnected() {
            logcatOnUI("Signal Server Connecting !")
        }

        override fun onClosse() {
            logcatOnUI("Signal Server close")
        }

        override fun onRemoteNewPeer(message: JSONObject?) {
            //收到信令 发起连接
            try {
                mRemoteUserId = message?.getString("remoteUid")
            } catch (e: JSONException) {
                Log.e(TAG, "onRemoteNewPeer JSON parsing error: " + e.toString())
            }

            logcatOnUI("Remote User Joined: " + mRemoteUserId)
            mExecutor!!.execute(Runnable {
                //14. createOffer 的时机 同 js端一样的
                doStartCall()
            })
        }

        override fun onResponseJoin(message: JSONObject?) { //
            try {
                mRemoteUserId = message?.getString("remoteUid")
            } catch (e: JSONException) {
                Log.e(TAG, "onResponseJoin JSON parsing error: " + e.toString())
            }

            logcatOnUI("Receive Remote User Joined")
        }

        override fun onRemotePeerLeave(message: JSONObject?) {
            logcatOnUI("Receive Remote Hanup  ...")
            hanup()
        }

        override fun onRemoteOffer(message: JSONObject?) {
            //收到后加入的用户进来的信息
            logcatOnUI("Receive Remote Offer ...")
            if (mPeerConnection == null) {
                mPeerConnection = createPeerConnection()
            }
            try {
                val sdpJson = JSONObject(message?.getString("msg"))
                val description = sdpJson.getString("sdp")
                doAnswerCall(description)
            } catch (e: JSONException) {
                e.printStackTrace()
            }
        }

        override fun onRemoteAnswer(message: JSONObject?) {
            //18.收到 answer
            logcatOnUI("Receive Remote Answer ...")
            try {
                val sdpJson = JSONObject(message?.getString("msg"))
                val description = sdpJson.getString("sdp")
                mPeerConnection!!.setRemoteDescription(
                    SimpleSdpObserver(),
                    SessionDescription(SessionDescription.Type.ANSWER, description)
                )
            } catch (e: JSONException) {
                e.printStackTrace()
            }
            updateCallState(false)
        }

        override fun onRemoteCandidate(message: JSONObject?) {
            logcatOnUI("Receive Remote Candidate ...")
            try {
                val candidateString = message?.getString("msg")
                val iceCandidate = toJavaCandidate(JSONObject(candidateString))
                mPeerConnection!!.addIceCandidate(iceCandidate)
            } catch (e: JSONException) {
                e.printStackTrace()
            }
        }
    }

    private fun logcatOnUI(msg: String) {
        Log.i(TAG, msg)
        runOnUiThread(object : Runnable {
            override fun run() {
                val output = mLogcatView!!.getText().toString() + "\n" + msg
                mLogcatView!!.setText(output)
            }
        })
    }

    private fun toJsonSessionDescription(sdp: SessionDescription?): String {
        Log.i(TAG, "toJsonSessionDescription")
        val json = JSONObject()

        var type = "offer"
        if (sdp?.type == SessionDescription.Type.OFFER) {
            type = "offer"
        } else if (sdp?.type == SessionDescription.Type.ANSWER) {
            type = "answer"
        } else if (sdp?.type == SessionDescription.Type.PRANSWER) {
            type = "pranswer"
        } else {
            type = "unkown"
            Log.e(TAG, "toJsonSessionDescription failed: unknown the sdp type")
        }
        val sdpDescription = sdp?.description

        try {
            json.put("sdp", sdpDescription)
            json.put("type", type)
        } catch (e: JSONException) {
            throw RuntimeException(e)
        }

        return json.toString()
    }

    private fun toJsonCandidate(candidate: IceCandidate): String {
        val json = JSONObject()

        try {
            json.put("id", candidate.sdpMid)
            json.put("label", candidate.sdpMLineIndex)
            json.put("candidate", candidate.sdp)
        } catch (e: JSONException) {
            throw RuntimeException(e)
        }

        return json.toString()
    }

    @Throws(JSONException::class)
    private fun toJavaCandidate(json: JSONObject): IceCandidate {
        return IceCandidate(
            json.getString("id"), json.getInt("label"), json.getString("candidate")
        )
    }

    companion object {
        private const val VIDEO_RESOLUTION_WIDTH = 640
        private const val VIDEO_RESOLUTION_HEIGHT = 480
        private const val VIDEO_FPS = 15

        private const val TAG = "CallActivity"

        //**************************************各种约束******************************************/
        private const val AUDIO_ECHO_CANCELLATION_CONSTRAINT = "googEchoCancellation"
        private const val AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT = "googAutoGainControl"
        private const val AUDIO_HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter"
        private const val AUDIO_NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression"

        const val VIDEO_TRACK_ID: String = "ARDAMSv0"
        const val AUDIO_TRACK_ID: String = "ARDAMSa0"
        const val LOCAL_STREAM_ID: String = "ARDAMS"

        // turn and stun
        private val iceServers: Array<MyIceServer>? =
            arrayOf<MyIceServer>( //        new MyIceServer("stun:47.106.181.107:3478"),
                //        new MyIceServer("turn:47.106.181.107:3478?transport=udp",
                //                "jack",
                //                "123456"),
                //        new MyIceServer("turn:47.106.181.107:3478?transport=tcp",
                //                "jack",
                //                "123456")
                MyIceServer("stun:192.168.1.5:3478"),
                MyIceServer(
                    "turn:192.168.1.5:3478?transport=udp",
                    "jack",
                    "123456"
                ),
                MyIceServer(
                    "turn:192.168.1.5:3478?transport=tcp",
                    "jack",
                    "123456"
                )
            )
    }
}
